/// <reference path="./renderPipeline.ts" />
/// <reference path="./renderContext.ts" />
namespace FIREFLYX {
    /** 渲染器队列数据 */
    class RenderQueueData {
        public rendererIDs: number[] = [];
        public materialCounts: number[] = [];
        public materials: Material[] = [];

        public clear() {
            this.rendererIDs.length = 0;
            this.materialCounts.length = 0;
            this.materials.length = 0;
        }
    }

    /** 渲染器队列收集器 */
    class RenderQueueCollect {
        private static readonly pool: Pool<RenderQueueData> = new Pool(RenderQueueData);

        private _dataMap: Map<number, RenderQueueData> = new Map();
        private _renderQueueIDs: number[] = [];
        private _rqGetOrder: number[] = [];
        private _rqDirty = false;

        /** 渲染队列数据 字典容器 */
        public get dataMap() { return this._dataMap; }

        /** 排序过的队列id */
        public get renderQueueIDs() {
            const rqs = this._renderQueueIDs;
            if (this._rqDirty) {
                this._rqDirty = false;
                rqs.length = 0;
                this._dataMap.forEach((v, k) => { rqs.push(k); });
                rqs.sort();
            }
            return rqs;
        }

        /**
         * 清理
         */
        public clear() {
            const _p = RenderQueueCollect.pool;
            const rqOrder = this._rqGetOrder;
            const len = rqOrder.length;
            //优化 对池的使用 ，反向循环遍历 delete
            for (let i = len - 1; i >= 0; i--) {
                const id = rqOrder[i];
                const data = this._dataMap.get(id) as RenderQueueData;
                data.clear();
                _p.delete(data);
            }
            this._dataMap.clear();
            this._rqGetOrder.length = 0;
        }

        /**
         * 获取RenderQueueData
         * @param renderQueueID 指定ID
         * @returns RenderQueueData对象
         */
        public getQueue(renderQueueID: number): RenderQueueData {
            //
            if (!this._dataMap.has(renderQueueID)) {
                this._rqDirty = true;
                const rq = RenderQueueCollect.pool.new();
                this._dataMap.set(renderQueueID, rq as RenderQueueData);
                this._rqGetOrder.push(renderQueueID);
            }
            return this._dataMap.get(renderQueueID) as RenderQueueData;
        }
    }

    class RendererCombData {
        private static readonly _pool = new Pool<RendererCombData>(RendererCombData, 256, (obj) => { obj.clear(); });
        /** 材质组信息池对象 */
        public static get pool() { return this._pool; }

        public r: Nullable<Renderer>;
        public mtrStartIdx: number = 0;
        public mtrCount: number = 0;
        public clear() {
            this.r = null;
            this.mtrStartIdx = 0;
            this.mtrCount = 0;
        }
    }

    /** 材质组信息 */
    class MaterialGroupInfo {
        private static readonly _pool = new Pool<MaterialGroupInfo>(MaterialGroupInfo, 256, (obj) => { obj.clear(); });
        /** 材质组信息池对象 */
        public static get pool() { return this._pool; }

        /** 主材质 */
        public mainMaterial: Nullable<Material>;
        /** 透明材质 */
        public transparents: Material[];
        /** 阴影铸造材质 */
        public shadowCasters: Material[];

        /** 清空, 方便重新使用*/
        public clear() {
            this.mainMaterial = null;
            if (this.transparents) this.transparents.length = 0;
            if (this.shadowCasters) this.shadowCasters.length = 0;
        }
    }

    /** 渲染对象的材质信息 */
    class RendererMaterialInfo {
        private static readonly _pool = new Pool<RendererMaterialInfo>(RendererMaterialInfo, 64, (obj) => { obj.clear(); });
        /** 材质组信息池对象 */
        public static get pool() { return this._pool; }

        /** 渲染器的ID值 */
        public rendererID: number = NaN;
        /** 材质组信息列表 */
        private _mGInfos: MaterialGroupInfo[] = [];
        /** 阴影投射材质组索引列表 */
        public shadowCasterMGIndexs: number[] = [];
        /** 透明混合材质组索引列表 */
        public transparentMGIndexs: number[] = [];

        /** 材质组信息列表 长度 */
        public get mGInfoLength() { return this._mGInfos.length; }

        /** 添加新的 材质组信息 到列表，并返回该对象 */
        public addMGInfo(): MaterialGroupInfo {
            const mgInfo = MaterialGroupInfo.pool.new() as MaterialGroupInfo;
            this._mGInfos.push(mgInfo);
            return mgInfo;
        }

        /** 获取 材质组信息 */
        public getMGInfo(index: number): Nullable<MaterialGroupInfo> {
            let len = this._mGInfos.length;
            if (isNaN(index) || index < 0 || index >= len) return null;
            return this._mGInfos[index];
        }

        /** 清空, 方便重新使用*/
        public clear() {
            this.rendererID = NaN;
            for (let i = 0, len = this._mGInfos.length; i < len; i++) {
                MaterialGroupInfo.pool.delete(this._mGInfos[i]);
            }
            this._mGInfos.length = 0;
            this.shadowCasterMGIndexs.length = 0;
            this.transparentMGIndexs.length = 0;
        }
    }

    type DebugFrameType = "" | "shadowMap" | "camDepthMap" | "screenShadowMap";

    /**
     * 引擎内建 前向渲染 管线
     */
    export class ForwardPipeline extends RenderPipeline {
        //渲染细节设定
        //渲染 Primitive:
        //1. 一个有效 渲染器 有 1 至 多个 渲染 Primitive
        //2. 每个 渲染primitive 渲染使用的 材质组 与 渲染器的材质组列表 顺序对齐。
        //3. 出现 Primitive 数大于 材质组的数量 ，用 最后的 材质组 填充对齐
        private static readonly HELP_VEC3 = new Vector3();
        private static readonly HELP_VEC3_1 = new Vector3();

        private readonly help_MtrArr: Material[] = [];
        private readonly help_rdCombArr: RendererCombData[] = [];
        private readonly help_RVDataResult: IRendererVDataResult = {
            material: 0,
            lenght: 0,
            bufferOffset: 0,
            elementType: -1,
            drawMode: WebGL2RenderingContext.TRIANGLES,
            frontFace: WebGL2RenderingContext.CCW
        };
        private readonly help_StrBoolMap: Map<string, boolean> = new Map();

        private _currRenderCtx: RenderContext;
        private _ignBlcokNames: string[];
        private _forwardRenderQueueC: RenderQueueCollect = new RenderQueueCollect();
        private _currStencilState: Nullable<StencilState>;
        private _currBlendState: Nullable<BlendState>;
        private _currDepthState: Nullable<DepthState>;
        private _currRasterState: Nullable<RasterState>;
        private _fullClipQuadMesh: Mesh;
        private _shadowDebugTexMtr: Nullable<Material>;
        private _screenShadowMapMtr: Material;
        private _varianceShadowMapMtr: Material;
        private _depthMapMtr: Material;
        private _fullScreenImageMtr: Material;
        private _fullScreenViewPortRect = new Rect(0, 0, 1, 1);
        private _mainShadowMapList: RenderTexture2D[] = [];
        private _mainVSMList: RenderTexture2D[] = [];
        private _cameraFrustumPointsList: Vector3[][] = [];
        private _lightFrustumDataList: LightFrustumData[] = [];
        private _mainShadowCSMSplit = new Float32Array(4);      //[split0,split1,split2,shadowDistance]
        private _camDepthTex: Nullable<RenderTexture2D>;
        private _screenShadowTex: Nullable<RenderTexture2D>;
        private _resolution = new Float32Array(2);
        private _fboResolution = new Float32Array(2);
        private _shadowCasterRList: RendererMaterialInfo[] = [];
        private _rendererMInfoMap: Map<number, RendererMaterialInfo> = new Map();
        private _drawDebugFrame: DebugFrameType = "";    //渲染 调试帧类型(为空 "" 不执行debug绘制)
        private _renderImageComps: IOnRenderImage[] = [];
        private _builtinUniformMap: Map<string, () => (UnifromNormalValue | UnifromNormalValue[] | null)> = new Map();

        // eslint-disable-next-line no-useless-constructor
        constructor() {
            super();
            const gl = App.webgl2;

            this._ignBlcokNames = [RenderContext.UBO_CAMERA_BLOCK_NAME, RenderContext.UBO_MODEL_BLOCK_NAME, RenderContext.UBO_LIGHT_BLOCK_NAME];
            //
            this._fullClipQuadMesh = DefaultAsset.GetMesh(DefMesh.QUAD_FULL_CLIP);

            //
            this._fullScreenViewPortRect.set(0, 0, gl.canvas.width, gl.canvas.height);

            //工具材质
            this._screenShadowMapMtr = new Material("screenShadowMap");
            this._screenShadowMapMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW);

            this._varianceShadowMapMtr = new Material("varianceShadowMap");
            this._varianceShadowMapMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_VSM);

            this._depthMapMtr = new Material("depthMap");
            this._depthMapMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_BASE_DEPTH);

            this._fullScreenImageMtr = new Material("fullScreen");
            this._fullScreenImageMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_POSTEFF_IMAGE);

            // this._skyBoxMtr = new Material("skyBox");
            // this._skyBoxMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_SKY_BOX);

            //内建纹理绑定
            const _buMap = this._builtinUniformMap;
            _buMap.set(BuiltinUniform.CAMERA_DEPTH_TEXTURE, this.getCameraDepthTexture.bind(this));

        }

        /** 获取相机的深度纹理 */
        private getCameraDepthTexture() {
            let result = null;
            if (this._camDepthTex) result = this._camDepthTex.depthStencilTexture;
            return result;
        }

        /**
         * 绑定内建uniform变量函数
         * @param renderCtx 
         * @param unbind 是否解绑
         */
        private bindBuiltinUniforms(renderCtx: RenderContext, unbind: boolean) {
            const _map = renderCtx.builtinUniformMap;
            this._builtinUniformMap.forEach((v, k) => {
                _map.set(k, unbind ? null : v);
            });
        }

        private setRendererVDataResult(rVDataResult: IRendererVDataResult) {
            rVDataResult.material = 0;
            rVDataResult.lenght = 0;
            rVDataResult.bufferOffset = 0;
            rVDataResult.elementType = -1;
            rVDataResult.drawMode = WebGL2RenderingContext.TRIANGLES;
            rVDataResult.frontFace = WebGL2RenderingContext.CCW;
        }

        private refreshResolutionVal() {
            const gl = App.webgl2;
            this._resolution[0] = gl.canvas.width;
            this._resolution[1] = gl.canvas.height;
        }

        private enableFBO(fbo: WebGLFramebuffer) {
            const gl = App.webgl2;
            gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
        }

        private disableFBO() {
            const gl = App.webgl2;
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        }

        /** 清理gl的状态 */
        private closeGLState() {
            const gl = App.webgl2;
            gl.bindVertexArray(null);
            gl.bindTexture(gl.TEXTURE_2D, null);
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        }

        /** 设置 webgl viewPort */
        private setGLViewPort(viewRect: Rect) {
            const gl = App.webgl2;
            //viewport set
            gl.viewport(viewRect.x, viewRect.y, viewRect.w, viewRect.h);
        }

        /** 获取 mian （深度）阴影图 */
        private getRTMainShadowMap(index: number) {
            if (index < 0) index = 0;
            if (!this._mainShadowMapList[index]) {
                // this._mainShadowMapList[index] = new RenderTexture2D(`mainShadowTarget${index}`, false, true);
                let rt = RenderTexture2D.GetTemporary();
                rt.setFBTexEnable(false, true);
                this._mainShadowMapList[index] = rt;
            }
            return this._mainShadowMapList[index];
        }

        /** 获取 mian VSM （颜色）阴影图 */
        private getRTMainVSM(index: number) {
            if (index < 0) index = 0;
            if (!this._mainVSMList[index]) {
                let rt = RenderTexture2D.GetTemporary();
                rt.paramSetting.linear = true;
                rt.setFBTexEnable(true);
                this._mainVSMList[index] = rt;
            }
            return this._mainVSMList[index];
        }

        /** 获取 mian 阴影图 */
        private getMainShadowMap(index: number) {
            return this._currRenderCtx.isVSM ? this.getRTMainVSM(index) : this.getRTMainShadowMap(index).depthStencilTexture as TextureBase;
        }

        /** 设置材质CSM 属性 */
        private setMateralCSMProperty(_lihgt: Light, _mtr: Material) {
            const l = _lihgt;
            //u_lightVPMatrix 计算
            const lightVPMatrix0 = l != null ? l.getViewProjMatrix(0) : null;
            const lightVPMatrix1 = l != null ? l.getViewProjMatrix(1) : null;
            const lightVPMatrix2 = l != null ? l.getViewProjMatrix(2) : null;
            const lightVPMatrix3 = l != null ? l.getViewProjMatrix(3) : null;
            _mtr.setProperty(`u_lightVPMatrix_${0}`, lightVPMatrix0);
            _mtr.setProperty(`u_mainShadowMap_${0}`, this.getMainShadowMap(0));
            _mtr.setProperty(`u_lightVPMatrix_${1}`, lightVPMatrix1);
            _mtr.setProperty(`u_mainShadowMap_${1}`, this.getMainShadowMap(1));
            _mtr.setProperty(`u_lightVPMatrix_${2}`, lightVPMatrix2);
            _mtr.setProperty(`u_mainShadowMap_${2}`, this.getMainShadowMap(2));
            _mtr.setProperty(`u_lightVPMatrix_${3}`, lightVPMatrix3);
            _mtr.setProperty(`u_mainShadowMap_${3}`, this.getMainShadowMap(3));
            _mtr.setProperty(`u_csm_split`, this._mainShadowCSMSplit);
        }

        /** 创建视锥点 */
        private makeFrustumPoints() {
            const result: Vector3[] = [];
            for (let i = 0; i < 8; i++) {
                result.push(new Vector3());
            }
            return result;
        }

        /**
         * 获取阴影图尺寸
         * @param resolution 
         * @returns 
         */
        private getShadowMapSize(resolution: ShadowMapResolution) {
            let size: number = 0;
            switch (resolution) {
                case ShadowMapResolution.LOW: size = 512; break;
                case ShadowMapResolution.MEDIUM: size = 1024; break;
                case ShadowMapResolution.HIGH: size = 2048; break;
                case ShadowMapResolution.VERY_HIGH: size = 4096; break;
                default:
            }
            return size;
        }

        /**
         * 计算 相机视锥 顶点数据
         * @param cam 相机
         * @param renderCtx RenderContext
         */
        private calcCamFrustumPoints(cam: Camera, renderCtx: RenderContext) {
            const csmCount = renderCtx.shadowCascades;
            const camFList = this._cameraFrustumPointsList;
            const lightFList = this._lightFrustumDataList;
            if (camFList.length < csmCount) {
                const defNum = csmCount - camFList.length;
                for (let i = 0; i < defNum; i++) {
                    camFList.push(this.makeFrustumPoints());
                    lightFList.push(new LightFrustumData());
                }
            }

            let lastFar = 0;
            for (let i = 0; i < csmCount; i++) {
                const splitRate = renderCtx.getShadowCascadeSplit(i);
                const n = lastFar;
                const f = renderCtx.shadowDistance * splitRate;
                cam.calcFrustumPoints(n, f, camFList[i]);
                lastFar = f;
            }
        }

        private setStencilState(stencilState: Nullable<StencilState>) {
            if (stencilState == this._currStencilState) return;
            const gl = App.webgl2;
            const sState = stencilState;
            if (sState == null) {
                gl.disable(gl.STENCIL);
            } else {
                gl.enable(gl.STENCIL);
                //front
                gl.stencilFuncSeparate(gl.FRONT, sState.funcFront, sState.refFront, sState.valueMaskFront);
                gl.stencilMaskSeparate(gl.FRONT, sState.writeMaskFront);
                gl.stencilOpSeparate(gl.FRONT, sState.failOpFront, sState.zFailOpFront, sState.passOpFront);
                //back
                gl.stencilFuncSeparate(gl.BACK, sState.funcBack, sState.refBack, sState.valueMaskBack);
                gl.stencilMaskSeparate(gl.BACK, sState.writeMaskBack);
                gl.stencilOpSeparate(gl.BACK, sState.failOpBack, sState.zFailOpBack, sState.passOpBack);
            }

            this._currStencilState = stencilState;
        }

        private setBlendState(blendState: Nullable<BlendState>) {
            if (blendState == this._currBlendState) return;
            const gl = App.webgl2;
            const bState = blendState;
            if (bState == null) {
                gl.disable(gl.BLEND);
            } else {
                gl.enable(gl.BLEND);
                const hasAlphaBlend = bState.dstAlpha != null && bState.srcAlpha != null && bState.equationAlpha != null;
                if (!hasAlphaBlend) {
                    gl.blendEquation(bState.equation);
                    gl.blendFunc(bState.src, bState.dst);
                } else {
                    gl.blendEquationSeparate(bState.equation, bState.equationAlpha);
                    gl.blendFuncSeparate(bState.src, bState.dst, bState.srcAlpha, bState.dstAlpha);
                }

            }
            this._currBlendState = blendState;
        }

        private setDepthState(depthState: DepthState) {
            //
            if (depthState == this._currDepthState) return;
            const gl = App.webgl2;
            //深度设置
            //深度写入
            gl.depthMask(depthState.zWrite);
            //深度测试
            if (depthState.zTestCompareFunc == null) {
                gl.disable(gl.DEPTH_TEST);
            } else {
                gl.enable(gl.DEPTH_TEST);
                gl.depthFunc(depthState.zTestCompareFunc);
            }

            this._currDepthState = depthState;
        }

        private setRasterState(rasterState: RasterState) {
            //
            if (rasterState == this._currRasterState) return;
            const gl = App.webgl2;
            //光栅化状态
            //面剔除
            const cMode = rasterState.cullMode;
            switch (cMode) {
                case CullMode.NONE: gl.disable(gl.CULL_FACE); break;
                case CullMode.BACK:
                    gl.enable(gl.CULL_FACE);
                    gl.cullFace(gl.BACK);
                    break;
                case CullMode.FRONT:
                    gl.enable(gl.CULL_FACE);
                    gl.cullFace(gl.FRONT);
                    break;
                default:
            }
            //多边形偏移
            gl.polygonOffset(rasterState.polygonOffsetFactor, rasterState.polygonOffsetUnits);

            this._currRasterState = rasterState;
        }

        /**
         * 排序 相机Z深度排序渲染队列数据,由于近及远
         * @param rqData 队列数据
         * @param rendererMap 所有render的Map
         * @param viewMatrix 相机view矩阵
         * @param sortReverse 排序颠倒
         */
        private sortCamZRenderQueueData(rqData: RenderQueueData, rendererMap: Map<number, Renderer>, viewMatrix: Matrix, sortReverse = false) {
            if (!viewMatrix || !rendererMap) return;
            let tempComb: RendererCombData[] = this.help_rdCombArr;
            //填充排序数据
            const rids = rqData.rendererIDs;
            const mtrCounts = rqData.materialCounts;
            const mtrs = rqData.materials;
            let _mtrIdx = 0;
            for (let i = 0, len = rids.length; i < len; i++) {
                const r = rendererMap.get(rids[i]) as Renderer;
                const mtrIdx = _mtrIdx;
                const mtrCount = mtrCounts[i];
                _mtrIdx += mtrCount;
                //RendererCombData
                const rcData = RendererCombData.pool.new() as RendererCombData;
                rcData.r = r;
                rcData.mtrStartIdx = mtrIdx;
                rcData.mtrCount = mtrCount;
                tempComb.push(rcData);
            }

            //开始排序
            tempComb.sort((a, b) => {
                if (a == b) return 0;
                if (!a || !a.r || !a.r.entity) return sortReverse ? 1 : -1;
                if (!b || !b.r || !b.r.entity) return sortReverse ? -1 : 1;
                //
                const aPos = a.r.entity.transform.localPostion;
                const bPos = b.r.entity.transform.localPostion;
                const v3A = ForwardPipeline.HELP_VEC3;
                const v3B = ForwardPipeline.HELP_VEC3_1;
                Vector3.TransformCoordinatesToRef(aPos, viewMatrix, v3A);
                Vector3.TransformCoordinatesToRef(bPos, viewMatrix, v3B);
                //view 空间 z 深度排序
                return sortReverse ? v3B.z - v3A.z : v3A.z - v3B.z;
            });

            //
            const tempMtrs: Material[] = this.help_MtrArr;
            tempMtrs.length = 0;
            //梳理到 RenderQueueData
            for (let i = 0, len = tempComb.length; i < len; i++) {
                //
                const val = tempComb[i] as RendererCombData;
                const r = val.r;
                if (!r) continue;
                const mtrIdx = val.mtrStartIdx;
                const mtrCount = val.mtrCount;

                //renderIDs
                rids[i] = r.getInstanceID();
                //mtrCounts
                mtrCounts[i] = mtrCount;
                //mtrIndex
                for (let j = 0, len1 = mtrCount; j < len1; j++) {
                    const mtr = mtrs[mtrIdx + j];
                    tempMtrs.push(mtr);
                }
            }
            //copyFrom
            // mtrs.copyFrom(tempMtrs);
            let len = tempMtrs.length;
            mtrs.length = 0;
            for (let i = 0; i < len; i++) {
                mtrs[i] = tempMtrs[i];
            }

            //清理
            const rcDataPool = RendererCombData.pool;
            for (let i = 0, len = tempComb.length; i < len; i++) {
                rcDataPool.delete(tempComb[i]);
            }
            tempComb.length = 0;
        }

        /**
         * 材质组信息整理分析
         */
        private materialGroupAnalyse(rendererMap: Map<number, Renderer>, cpRendererIDs: number[],
            mgInfoSetMap: Map<number, RendererMaterialInfo>, shadowCasterRList: RendererMaterialInfo[]) {
            //遍历所有render
            // const rendererMap = renderCtx.rendererMap;
            const cprIDsLen = cpRendererIDs.length;
            let cprIdx = 0;
            while (cprIdx < cprIDsLen) {
                const id = cpRendererIDs[cprIdx];
                cprIdx++;
                if (id == null) continue;
                const r = rendererMap.get(id);
                if (!r) continue;

                let mgs = r.materialGroupList;
                let mgsLen = mgs.length;
                if (mgsLen == 0) return; //没有材质数据，跳过。
                let mgInfoSet = RendererMaterialInfo.pool.new() as RendererMaterialInfo;
                mgInfoSet.rendererID = id;
                //      每组 分配一个infoSet对象，放入一个字典
                mgInfoSetMap.set(id, mgInfoSet);
                let isShadowCasterR = false;
                //      遍历材质，分析整理到info对象
                for (let i = 0; i < mgsLen; i++) {
                    let info = mgInfoSet.addMGInfo();
                    let isShadowCasterM = false;
                    let isTransparentM = false;
                    //
                    let mg = mgs[i];
                    let msLen = mg.length;
                    //主材质
                    info.mainMaterial = mg[0];
                    //遍历 材质组
                    for (let j = 0; j < msLen; j++) {
                        let m = mg[j];
                        //透明材质
                        if (m.blendState != null) {
                            isTransparentM = true;
                            if (!info.transparents) {
                                info.transparents = [];
                            }
                            info.transparents.push(m);
                        }

                        //阴影投射
                        if (r.castShadows && m.lightMode == RenderPipeline.LM_SHADOW_CASTER) {
                            isShadowCasterM = true;
                            if (!info.shadowCasters) {
                                info.shadowCasters = [];
                            }
                            info.shadowCasters.push(m);
                        }

                        //整理安排队列
                        if (m.lightMode == RenderPipeline.LM_FORWARD || m.lightMode == RenderPipeline.LM_ALWAYS) {
                            const fRQ = this._forwardRenderQueueC.getQueue(m.renderQueue);
                            fRQ.materials.push(m);

                            if (fRQ.rendererIDs[fRQ.rendererIDs.length - 1] != id) {
                                fRQ.rendererIDs.push(id);
                                fRQ.materialCounts.push(1);
                            } else {
                                const lastIndex = fRQ.materialCounts.length - 1;
                                const lastVal = fRQ.materialCounts[lastIndex] as number;
                                fRQ.materialCounts[lastIndex] = lastVal + 1; //数增1
                            }
                        }

                    }

                    //阴影pass 收集 材质
                    if (isShadowCasterM) {
                        isShadowCasterR = true;
                        mgInfoSet.shadowCasterMGIndexs.push(i);
                    }
                    //      填充到队列（ 非透明、透明混合、透明测试）
                    if (isTransparentM) {
                        mgInfoSet.transparentMGIndexs.push(i);
                    }
                }

                //阴影pass 收集 renderer
                if (isShadowCasterR) {
                    shadowCasterRList.push(mgInfoSet);
                }
            }
        }

        /**
         * 相机深度图更新
         */
        private renderCamDepthTex(renderCtx: RenderContext) {
            //检查一下是否要 更新 相机深度图
            //light 检查
            const mainLight = renderCtx.lightCollector.mainLight as Light;
            let needMainLightShadow = mainLight != null && mainLight.entity != null;

            let needUpdateCamDepthTex2D = needMainLightShadow;
            let needUpdateCamDepthTexCube = false;

            const scrlist = this._shadowCasterRList;

            if (needUpdateCamDepthTex2D) {
                if (!this._camDepthTex) {
                    this._camDepthTex = RenderTexture2D.GetTemporary();
                    this._camDepthTex.setFBTexEnable(false, true, false);
                }
                //相机的深度图2D渲染
                this.renderCameraDepthTex2D(renderCtx, this._camDepthTex, scrlist);
            }

            if (needUpdateCamDepthTexCube) {
                this.renderCameraDepthTexCube();
            }
        }

        /**
         * 渲染整个队列
         * @param materials 材质列表
         * @param rendererIDs 渲染器列表
         * @param mtrCounts 材质数量列表
         * @param isTransparent 是否半透明混合
         */
        private renderByQueue(renderCtx: RenderContext, materials: Material[], rendererIDs: number[], mtrCounts: number[],
            isTransparent: boolean) {

            const rendererMap = renderCtx.rendererMap;
            if (!rendererMap) return;
            const gl = renderCtx.webgl2;
            const materialDirMap = this.help_StrBoolMap;
            materialDirMap.clear();
            const rVDataResult = this.help_RVDataResult;
            this.setRendererVDataResult(rVDataResult);
            this.refreshResolutionVal();

            //last cache
            let lastMaterial: Nullable<Material> = null;
            let lastShaderCompiled: Nullable<ShaderCompiled> = null;
            let lastFrontFace: Nullable<number> = null;

            let currMbuoy = 0;
            //遍历 元素
            for (let i = 0, len1 = rendererIDs.length; i < len1; i++) {
                const mbuoy = currMbuoy;
                const mCount = mtrCounts[i];
                if (mCount == null) continue;
                currMbuoy += mCount;
                const id = rendererIDs[i];
                if (id == null) continue;
                const r = rendererMap.get(id);
                if (!r) continue;
                const model = r.entity;
                if (!model) continue;
                //绑定ubo
                //灯光 设置提交 (每个模型最大接收光源有上限)
                //模型 设置提交
                const modelUBO = model.transform.ubo;
                model.transform.refreshUboData();
                gl.bindBufferRange(gl.UNIFORM_BUFFER, UBOBindingEnum.MODEL, modelUBO, 0, model.transform.uboBlockSize);

                //筛选渲染的材质
                //逐 mesh , 的subMesh 渲染
                const vDataLen = r.getVertexDataCount();
                let idx = 0;
                while (idx < vDataLen) {
                    const _idx = idx;
                    //增加计数
                    idx++;
                    //渲染单个 模型
                    //顶点数据 设置提交
                    r.uploadVertexData(_idx, rVDataResult);
                    const targetMIdx = rVDataResult.material;
                    //获取相应的材质组
                    // const mIdx = mbuoy + (_idx < mCount ? _idx : 0);
                    const mIdx = mbuoy + (!isNaN(targetMIdx) && targetMIdx < mCount && targetMIdx > 0 ? targetMIdx : 0);
                    const m = materials[mIdx];
                    if (!m) continue;
                    const sh = m.shader;
                    const cSh = sh.getCompiled(m.getKeywordID());
                    if (!cSh) continue;
                    const sp = cSh.program;
                    if (!sp) continue;

                    //填充材质数据
                    if (!materialDirMap.get(m.UUID)) {
                        materialDirMap.set(m.UUID, true);

                        //阴影数据
                        //是否接收阴影
                        let receiveShadows = r.receiveShadows && !isTransparent && this._shadowCasterRList.length > 0;

                        //屏幕空间 阴影图 属性设置
                        let screenShadowMap: Nullable<RenderTexture2D> = null;
                        let resolution: Nullable<Float32Array> = null;
                        if (receiveShadows) {   //***之后要用 shader 关键字 切换变体的方式处理
                            screenShadowMap = this._screenShadowTex;
                            resolution = this._resolution;
                        }
                        m.setProperty("u_screenShadowTex", screenShadowMap);
                        m.setProperty("u_resolution", resolution);
                    }

                    //这里需要好好考虑，比如 shader 变体 、 skin 和 非skin 怎么处理
                    const sameWithLastMaterial = lastMaterial == m;
                    //相同的 shader 分支（变体）
                    if (!sameWithLastMaterial || lastShaderCompiled != cSh) {
                        gl.useProgram(sp);

                        //ubo index 绑定到 shaderProgram-------------
                        // const invalidBlockIdx = 4294967295; //getUniformBlockIndex 无效返回值
                        if (cSh.unifBlockNameIndexMap[RenderContext.UBO_CAMERA_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_CAMERA_BLOCK_NAME], UBOBindingEnum.CAMERA);
                        }
                        if (cSh.unifBlockNameIndexMap[RenderContext.UBO_MODEL_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_MODEL_BLOCK_NAME], UBOBindingEnum.MODEL);
                        }
                        if (cSh.unifBlockNameIndexMap[RenderContext.UBO_LIGHT_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_LIGHT_BLOCK_NAME], UBOBindingEnum.LIGHT);
                        }

                        //纹理Activator
                        renderCtx.textrueActivator.reset();
                        //材质 uniform 上传---------------------------
                        m.uploadUniform(renderCtx);
                    }

                    //和上一个渲染的材质是否为同一个,跳过状态修改
                    if (!sameWithLastMaterial) {

                        //材质状态 设置提交----------------------------
                        this.setStencilState(m.stencilState);
                        this.setBlendState(m.blendState);
                        this.setDepthState(m.depthState);
                        this.setRasterState(m.rasterState);
                    }

                    //正面顺序
                    if (rVDataResult.frontFace != lastFrontFace) {
                        gl.frontFace(rVDataResult.frontFace);
                    }

                    //绘制
                    const drawInsCount = 1; //GPUInstance 预留
                    const isElementMode = rVDataResult.elementType != -1;
                    if (isElementMode) {
                        gl.drawElementsInstanced(rVDataResult.drawMode, rVDataResult.lenght, rVDataResult.elementType, rVDataResult.bufferOffset, drawInsCount);
                    } else {
                        gl.drawArraysInstanced(rVDataResult.drawMode, rVDataResult.bufferOffset, rVDataResult.lenght, drawInsCount);
                    }

                    //cache last
                    lastMaterial = m;
                    lastShaderCompiled = cSh;
                    lastFrontFace = rVDataResult.frontFace;
                }

            }

            //清理
            materialDirMap.clear();
        }

        /** 渲染forwardPass */
        private renderForwardPass(renderCtx: RenderContext, camera: Camera) {
            const rendererMap = renderCtx.rendererMap;
            if (!rendererMap) return;
            this.setGLViewPort(renderCtx.curCamPixelViewPort);

            //前向渲染通道 渲染器队列收集器
            const forwardRQC = this._forwardRenderQueueC;

            //遍历 队列
            const rqIds = forwardRQC.renderQueueIDs;
            const maxTransparentRQ = RenderContext.RENDER_QUEUE_TRANSPARENT + 2000;
            const maxAlphaTestRQ = RenderContext.RENDER_QUEUE_ALPHA_TEST + 2000;
            for (let i = 0, len = rqIds.length; i < len; i++) {
                const rqid = rqIds[i];
                if (rqid == null) continue;
                const data = forwardRQC.dataMap.get(rqid);
                if (data == null) continue;

                //  按Z排序
                //  透明强制排序
                //  非透明可选 （排序CPU 负荷，不排序 GPU负荷）
                let isTransparent = false;
                if (rqid >= RenderContext.RENDER_QUEUE_ALPHA_TEST && rqid < maxAlphaTestRQ) {
                    //alpha test 物
                } else if (rqid >= RenderContext.RENDER_QUEUE_TRANSPARENT && rqid < maxTransparentRQ) {
                    isTransparent = true;
                    //是透明物 ， 需要相机z 深度排序，需要由远及近 ,所以 sortReverse
                    this.sortCamZRenderQueueData(data, renderCtx.rendererMap as any, camera.getViewMatrix(), true);
                }
                this.renderByQueue(renderCtx, data.materials, data.rendererIDs, data.materialCounts, isTransparent);
            }

            //clear
            forwardRQC.clear();
            //绘制完毕清理状态
            this.closeGLState();
        }

        /** 渲染阴影投射通道 */
        private renderShadowCasterPass(renderCtx: RenderContext) {
            const scrlist = this._shadowCasterRList;
            if (scrlist.length < 1) return; //不需要 shadow

            const mainLight = renderCtx.lightCollector.mainLight as Light;

            let needMainLightShadow = mainLight != null && mainLight.entity != null;
            const needAddLightShadow = false;

            if (!needMainLightShadow && !needAddLightShadow) return;    //不需要 shadow

            //主光源
            if (needMainLightShadow) {
                //阴影的灯光级联深度图渲染
                this.renderDirLightCSM(renderCtx, scrlist, this._mainShadowCSMSplit,
                    this._lightFrustumDataList, this._cameraFrustumPointsList, mainLight);
            }

            //addLight 阴影

            //其他光源

            //
            if (!this._screenShadowTex) {
                this._screenShadowTex = RenderTexture2D.GetTemporary();
                this._screenShadowTex.setFBTexEnable(true);
            }

            //汇集阴影 到 屏幕空间
            this.renderCollectShadow(renderCtx, this._screenShadowTex, this._camDepthTex as RenderTexture2D, this._screenShadowMapMtr);

            //绘制完毕清理状态
            this.closeGLState();
            //关闭FBO
            this.disableFBO();
        }

        /** 屏幕空间阴影Map 到 屏幕空间方差阴影Map*/
        private renderVSM(lightDepthMap: TextureBase, vsm: RenderTexture2D, ishorizontalPass: boolean = false) {
            //材质
            let mat = this._varianceShadowMapMtr;
            this._fboResolution[0] = vsm.width;
            this._fboResolution[1] = vsm.width;

            mat.setProperty("u_resolution", this._fboResolution);
            mat.setProperty("u_samples", this._currRenderCtx.vsmSampleCount);
            mat.setProperty("u_radius", this._currRenderCtx.vsmSampleRadius);
            mat.setProperty("u_horizontalPass", ishorizontalPass ? 1 : 0);

            Graphics.Blit(lightDepthMap, vsm, mat);
        }

        /** 渲染 汇集阴影 （输出 屏幕空间阴影Map）*/
        private renderCollectShadow(renderCtx: RenderContext, screenSM: RenderTexture2D, camDepthTex: RenderTexture2D, _mtr: Material) {
            //所有阴影绘制到屏幕空间
            const gl = App.webgl2;
            const viewPort = renderCtx.curCamPixelViewPort;
            screenSM.setSize(viewPort.w, viewPort.h);   //设置纹理尺寸
            this._fboResolution[0] = viewPort.w;
            this._fboResolution[1] = viewPort.h;
            this.enableFBO(screenSM.fbo);
            //viewPort
            this.setGLViewPort(viewPort);
            //清理 
            gl.clearColor(1, 1, 1, 1);    //backclear color
            gl.clearDepth(1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            //主光源 阴影处理
            const mainLight = renderCtx.lightCollector.mainLight;
            if (mainLight != null) {
                const tex = camDepthTex.depthStencilTexture as TextureBase;
                //材质参数设置
                _mtr.setProperty("u_mainTex", tex);
                //u_resolution
                this.refreshResolutionVal();
                _mtr.setProperty("u_resolution", this._resolution);
                //shadow CSM 属性
                this.setMateralCSMProperty(renderCtx.lightCollector.mainLight as Light, _mtr);
                //渲染
                const quadMesh = this._fullClipQuadMesh;
                const sh = _mtr.shader;
                const cSH = sh.getCompiled(_mtr.getKeywordID()) as ShaderCompiled;
                const sp = cSH.program as WebGLProgram;

                //WebGLProgram use
                gl.useProgram(sp);
                //vao
                gl.bindVertexArray(quadMesh.VAO);   //启用vao
                //纹理Activator
                renderCtx.textrueActivator.reset();
                //uniform 数据提交
                _mtr.uploadUniform(renderCtx);

                //绘制贴屏片 后渲染效果
                gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);

                // //绘制完毕清理状态
                // this.closeGLState();

                //清理材质
                _mtr.setProperty("u_mainTex", null);
                _mtr.setProperty("u_resolution", this._resolution);
            }
            //close
            this.disableFBO();
            this.closeGLState();
        }

        /**
         * 渲染 相机2D 到深度纹理
         * @param renderCtx 
         * @param rendererList 渲染器列表
         * @param vpMatrix vp矩阵
         * @param skipTransparent 是否跳过半透明混合材质
         */
        private renderDepthTex2DByList(renderCtx: RenderContext, rendererList: RendererMaterialInfo[], vpMatrix: Matrix, skipTransparent = false) {
            const gl = App.webgl2;
            const rVDataResult: IRendererVDataResult = {
                material: 0,
                lenght: 0,
                bufferOffset: 0,
                elementType: -1,
                drawMode: gl.TRIANGLES,
                frontFace: gl.CCW
            };
            const rendererMap = renderCtx.rendererMap as Map<number, Renderer>;
            //      渲染状态统一设置 , shadowCaster 统一模式绘制
            this.setStencilState(null);
            this.setBlendState(null);
            this.setDepthState(DepthState.getState(true));
            //
            let lastFrontFace: Nullable<number> = null;
            let isFirstUseDepthMtr = true;
            //材质
            const m = this._depthMapMtr;
            const sh = m.shader;
            const cSh = sh.getCompiled(m.getKeywordID()) as ShaderCompiled;
            const sp = cSh.program as WebGLProgram;
            m.setProperty(`u_viewProjMtx`, vpMatrix);
            //      遍历renderer渲染模型
            for (let i = 0, len = rendererList.length; i < len; i++) {
                //
                const rmi = rendererList[i];
                if (skipTransparent && rmi.transparentMGIndexs.length > 0) continue;    //跳过透明物的渲染
                const r = rendererMap.get(rmi.rendererID) as Renderer;
                const rpCount = r.getVertexDataCount();
                const model = r.entity as GameObject;
                //to Refresh data
                model.transform.getMatrix();
                //遍历 render 所有的 VertexData ，并用 深度材质渲染一遍
                for (let j = 0, len1 = rpCount; j < len1; j++) {
                    //
                    //绑定ubo
                    //灯光 设置提交 (每个模型最大接收光源有上限)
                    //模型 设置提交
                    const modelUBO = model.transform.ubo;
                    model.transform.refreshUboData();
                    gl.bindBufferRange(gl.UNIFORM_BUFFER, UBOBindingEnum.MODEL, modelUBO, 0, model.transform.uboBlockSize);

                    //渲染
                    r.uploadVertexData(j, rVDataResult);

                    if (isFirstUseDepthMtr) {
                        isFirstUseDepthMtr = false;
                        gl.useProgram(sp);
                        if (cSh.unifBlockNameIndexMap[RenderContext.UBO_MODEL_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_MODEL_BLOCK_NAME], UBOBindingEnum.MODEL);
                        }

                        this.setRasterState(m.rasterState);

                        //纹理Activator
                        renderCtx.textrueActivator.reset();
                        m.uploadUniform(renderCtx);
                    }

                    //正面顺序
                    if (rVDataResult.frontFace != lastFrontFace) {
                        gl.frontFace(rVDataResult.frontFace);
                    }

                    //绘制
                    const drawInsCount = 1;
                    const isElementMode = rVDataResult.elementType != -1;
                    if (isElementMode) {
                        gl.drawElementsInstanced(rVDataResult.drawMode, rVDataResult.lenght, rVDataResult.elementType, rVDataResult.bufferOffset, drawInsCount);
                    } else {
                        gl.drawArraysInstanced(rVDataResult.drawMode, rVDataResult.bufferOffset, rVDataResult.lenght, drawInsCount);
                    }

                    //last cache
                    lastFrontFace = rVDataResult.frontFace;
                }
            }
        }

        /**
         * 渲染 到阴影纹理
         * @param renderCtx 
         * @param rendererList 渲染器列表
         * @param vpMatrix vp矩阵
         */
        private renderShadowMapByList(renderCtx: RenderContext, rendererList: RendererMaterialInfo[], vpMatrix: Matrix, skipTransparent = false) {
            const gl = App.webgl2;
            const rVDataResult = this.help_RVDataResult;
            this.setRendererVDataResult(rVDataResult);
            const rendererMap = renderCtx.rendererMap as Map<number, Renderer>;
            //      渲染状态统一设置 , shadowCaster 统一模式绘制
            this.setStencilState(null);
            this.setBlendState(null);
            this.setDepthState(DepthState.getState(true));
            //
            const materialDirMap = this.help_StrBoolMap;
            materialDirMap.clear();
            let lastFrontFace: Nullable<number> = null;
            let lastMaterial: Nullable<Material> = null;
            //u_lightVPMatrix 计算
            // const lightVPMatrix = light.getViewProjMatrix(csmIndex);
            const lightVPMatrix = vpMatrix;

            //      遍历renderer渲染模型
            for (let i = 0, len = rendererList.length; i < len; i++) {
                //
                const rmi = rendererList[i];
                const scMGIdxs = rmi.shadowCasterMGIndexs;
                const r = rendererMap.get(rmi.rendererID) as Renderer;
                if (skipTransparent && rmi.transparentMGIndexs.length > 0) continue;    //跳过透明物的渲染
                const rpCount = r.getVertexDataCount();
                for (let j = 0, len1 = scMGIdxs.length; j < len1; j++) {
                    const scMGIdx = scMGIdxs[j];
                    let pIdx = scMGIdx >= rpCount ? rpCount - 1 : scMGIdx;  //处理 子网格数 少于 材质数的情况
                    const mgInfo = rmi.getMGInfo(scMGIdx) as MaterialGroupInfo;
                    const model = r.entity as GameObject;
                    const m = mgInfo.shadowCasters[0];  //暂时处理，材质组中 多个shadowCasters 情况要处理？
                    const mainM = mgInfo.mainMaterial;
                    const sh = m.shader;
                    const cSh = sh.getCompiled(m.getKeywordID());
                    if (!cSh) continue;
                    const sp = cSh.program;
                    if (!sp) continue;
                    //填充阴影数据
                    if (!materialDirMap.get(m.UUID)) {
                        materialDirMap.set(m.UUID, true);
                        //vp矩阵 属性
                        const lVPMatName = `u_viewProjMtx`;
                        const mainTexName = `u_mainTex`;
                        const mainColorName = `u_mainColor`;
                        const resolutionName = `u_resolution`;
                        m.setProperty(lVPMatName, lightVPMatrix);
                        //alpha 属性
                        let mainTex: Nullable<Texture2D> = null;
                        let mainColor: Nullable<Color4> = null;
                        if (mainM) {
                            mainTex = mainM.getProperty(mainTexName) as any;
                            mainColor = mainM.getProperty(mainColorName) as any;
                        }
                        m.setProperty(mainTexName, mainTex);
                        m.setProperty(mainColorName, mainColor);
                        m.setProperty(resolutionName, this._fboResolution);
                    }

                    //to Refresh data
                    model.transform.getMatrix();

                    //绑定ubo
                    //灯光 设置提交 (每个模型最大接收光源有上限)
                    //模型 设置提交
                    const modelUBO = model.transform.ubo;
                    model.transform.refreshUboData();
                    gl.bindBufferRange(gl.UNIFORM_BUFFER, UBOBindingEnum.MODEL, modelUBO, 0, model.transform.uboBlockSize);

                    //渲染
                    r.uploadVertexData(pIdx, rVDataResult);

                    if (m != lastMaterial) {
                        gl.useProgram(sp);
                        if (cSh.unifBlockNameIndexMap[RenderContext.UBO_MODEL_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_MODEL_BLOCK_NAME], UBOBindingEnum.MODEL);
                        }
                        // gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_CAMERA_BLOCK_NAME], UBOBindingEnum.CAMERA);
                        // gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_LIGHT_BLOCK_NAME], UBOBindingEnum.LIGHT);

                        this.setRasterState(m.rasterState);

                        //纹理Activator
                        renderCtx.textrueActivator.reset();
                        m.uploadUniform(renderCtx);
                    }

                    //正面顺序
                    if (rVDataResult.frontFace != lastFrontFace) {
                        gl.frontFace(rVDataResult.frontFace);
                    }

                    //绘制
                    const drawInsCount = 1;
                    const isElementMode = rVDataResult.elementType != -1;
                    if (isElementMode) {
                        gl.drawElementsInstanced(rVDataResult.drawMode, rVDataResult.lenght, rVDataResult.elementType, rVDataResult.bufferOffset, drawInsCount);
                    } else {
                        gl.drawArraysInstanced(rVDataResult.drawMode, rVDataResult.bufferOffset, rVDataResult.lenght, drawInsCount);
                    }

                    //last cache
                    lastFrontFace = rVDataResult.frontFace;
                    lastMaterial = m;

                }
            }

            //清理
            materialDirMap.clear();
        }

        /**
         * 渲染 主方向光 的级联阴影图
         * @param renderCtx 
         * @param rendererList 渲染器列表
         * @param shadowCSMSplit 级联阴影段落区间划分参数
         * @param lightFrustumDataList 光的包围锥体数据列表
         * @param cameraFrustumPointsList 相机的包围锥体顶点数据列表
         * @param light 主方向光对象
         */
        private renderDirLightCSM(renderCtx: RenderContext, rendererList: RendererMaterialInfo[],
            shadowCSMSplit: Float32Array, lightFrustumDataList: LightFrustumData[],
            cameraFrustumPointsList: Vector3[][], light: Light,
        ) {
            const gl = App.webgl2;
            //阴影的灯光级联深度图渲染
            //填充级联划分值
            shadowCSMSplit.forEach((v, i, arr) => { arr[i] = 1; });
            for (let i = 0, len = renderCtx.shadowCascades - 1; i < len; i++) {
                shadowCSMSplit[i] = renderCtx.getShadowCascadeSplit(i);
            }
            //阴影Z方向的最大距离
            shadowCSMSplit[3] = renderCtx.shadowDistance;

            //计算 灯光视锥数据 (级联阴影)
            const csmCount = renderCtx.shadowCascades;
            for (let i = 0; i < csmCount; i++) {
                const lFData = lightFrustumDataList[i];
                const camF = cameraFrustumPointsList[i];
                Light.calcDirLightFrustumData(camF, (light.entity as GameObject).transform.rotation, lFData);
                light.setVPMatrix(i, lFData.w, lFData.h, lFData.far, lFData.position);
            }
            //阴影图 尺寸
            const shaodwMapSize = this.getShadowMapSize(light.shadowMapResolution);
            const mTexW = shaodwMapSize;
            const mTexH = shaodwMapSize;

            //view port 设置
            gl.viewport(0, 0, mTexW, mTexH);

            let vsmHTempRT: RenderTexture2D = null as any;
            if (renderCtx.isVSM) {
                vsmHTempRT = RenderTexture2D.GetTemporary();
                vsmHTempRT.paramSetting.linear = true;
                vsmHTempRT.setFBTexEnable(true);
                vsmHTempRT.setSize(mTexW, mTexH);
            }

            //遍历所有阴影级联
            for (let csmIndex = 0; csmIndex < csmCount; csmIndex++) {
                // this.enableFBO(ForwardPipeline.MAIN_SHADOWCASTER_FBO, mTexW, mTexH);
                const mainShadowMap = this.getRTMainShadowMap(csmIndex);
                mainShadowMap.setSize(mTexW, mTexH);
                this._fboResolution[0] = mTexW;
                this._fboResolution[1] = mTexH;
                //  深度渲染到2d纹理
                //      激活深度FBO
                this.enableFBO(mainShadowMap.fbo);
                //设置深度 ZWrite状态
                this.setDepthState(DepthState.getState(true));
                gl.clearDepth(1.0);
                //clear depth , 深度 ZWrite 不为 true，清理是无效的
                gl.clear(gl.DEPTH_BUFFER_BIT);
                //      light 视锥剔除

                const lightVPMatrix = light.getViewProjMatrix(csmIndex) as Matrix;
                //渲染 depth 队列
                this.renderShadowMapByList(renderCtx, rendererList, lightVPMatrix);
                //VSM 模式
                if (renderCtx.isVSM) {
                    let vsm = this.getRTMainVSM(csmIndex);
                    vsm.setSize(mTexW, mTexH);
                    this.renderVSM(mainShadowMap.depthStencilTexture as TextureBase, vsmHTempRT, false);
                    this.renderVSM(vsmHTempRT, vsm, true);
                }
            }

            if (renderCtx.isVSM) {
                RenderTexture2D.ReleaseTemporary(vsmHTempRT);
            }

            //清理
            this.closeGLState();
        }

        /** 渲染 相机的深度2D纹理 */
        private renderCameraDepthTex2D(renderCtx: RenderContext, depthTex: RenderTexture2D, rendererList: RendererMaterialInfo[]) {
            const gl = App.webgl2;
            const viewPort = renderCtx.curCamPixelViewPort;
            depthTex.setSize(viewPort.w, viewPort.h);
            this._fboResolution[0] = viewPort.w;
            this._fboResolution[1] = viewPort.h;
            //view port 设置
            gl.viewport(0, 0, depthTex.width, depthTex.height);
            //fbo 状态开启
            this.enableFBO(depthTex.fbo);
            this.setDepthState(DepthState.getState(true));      //zwrite true 才能清理 depth
            //清理 
            gl.clearDepth(1.0);
            gl.clear(gl.DEPTH_BUFFER_BIT);
            //遍历绘制
            const vPMatrix = renderCtx.camViewProjMatrix;
            //渲染 depth 队列
            this.renderDepthTex2DByList(renderCtx, rendererList, vPMatrix, true);
            //fbo 状态关闭
            this.disableFBO();
            //clear
            this.closeGLState();
        }

        /** 渲染 相机的深度cube纹理 */
        private renderCameraDepthTexCube() {
            //
        }

        /** 渲染 FBO 到 后处理全屏效果*/
        private renderPostEffQuad(renderCtx: RenderContext, material: Material, viewPortRect: Rect) {
            const gl = renderCtx.webgl2;
            const quadMesh = this._fullClipQuadMesh;
            const sh = material.shader;
            const cSH = sh.getCompiled(material.getKeywordID()) as ShaderCompiled;
            const sp = cSH.program as WebGLProgram;
            //viewport set
            this.setGLViewPort(viewPortRect);
            //pass clear
            gl.clearColor(0, 0, 0, 0);    //backclear color
            gl.clearDepth(1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            //WebGLProgram use
            gl.useProgram(sp);
            //vao
            gl.bindVertexArray(quadMesh.VAO);   //启用vao
            //纹理Activator
            renderCtx.textrueActivator.reset();
            //uniform 数据提交
            material.uploadUniform(renderCtx);

            //绘制贴屏片 后渲染效果
            gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);

            //绘制完毕清理状态
            this.closeGLState();
        }

        /**
         * 渲染 调试帧
         * @param renderCtx 
         * @param debugType 调试类型
         */
        private renderDebugFrame(renderCtx: RenderContext, debugType: DebugFrameType) {
            const gl = renderCtx.webgl2;
            let shKey: string = "";
            let tex: Nullable<TextureBase> = null;
            let _mtr = this._shadowDebugTexMtr;
            switch (debugType) {
                case "shadowMap":
                    shKey = BUILTIN_SHADER_POSTEFF_DEPTH_TEX;
                    if (!_mtr) {
                        this._shadowDebugTexMtr = _mtr = new Material("shadowDepthMtr");
                        _mtr.shader = DefaultAsset.GetShader(shKey);
                    }

                    tex = this.getRTMainShadowMap(0).depthStencilTexture as TextureBase;
                    _mtr.setProperty("u_mainTex", tex);
                    break;
                case "camDepthMap":
                    shKey = BUILTIN_SHADER_POSTEFF_DEPTH_TEX;
                    if (!_mtr) {
                        this._shadowDebugTexMtr = _mtr = new Material("shadowDepthMtr");
                        _mtr.shader = DefaultAsset.GetShader(shKey);
                    }
                    if (this._camDepthTex) tex = this._camDepthTex.depthStencilTexture as TextureBase;
                    _mtr.setProperty("u_mainTex", tex);
                    _mtr.setProperty("u_depthScale", 500);
                    break;
                case "screenShadowMap":
                    shKey = BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW;

                    if (!_mtr) {
                        this._shadowDebugTexMtr = _mtr = new Material("shadowDepthMtr");
                    }
                    if (this._camDepthTex) tex = this._camDepthTex.depthStencilTexture as TextureBase;
                    _mtr.shader = DefaultAsset.GetShader(shKey);
                    _mtr.setProperty("u_mainTex", tex);
                    //u_resolution
                    let _resolution = new Vector2(gl.canvas.width, gl.canvas.height);
                    _mtr.setProperty("u_resolution", _resolution);
                    //shadow CSM 属性
                    this.setMateralCSMProperty(renderCtx.lightCollector.mainLight as Light, _mtr);
                    break;
                default:
            }
            //
            this._fullScreenViewPortRect.set(0, 0, gl.canvas.width, gl.canvas.height);
            this.renderPostEffQuad(renderCtx, this._shadowDebugTexMtr as Material, this._fullScreenViewPortRect);
            // Graphics.DrawFullQuad(this._shadowDebugTexMtr as Material, this._fullScreenViewPortRect);
            //
            // this.closeGLState();
        }

        /** 设置相机 */
        private useCamera(cam: Camera, renderCtx: RenderContext) {
            const gl = App.webgl2;
            //
            //相机 viewPort 设置
            const pViewPort = renderCtx.curCamPixelViewPort;
            const canvasW = gl.canvas.width;
            const canvasH = gl.canvas.height;
            const vpRect = cam.viewPortRect;
            pViewPort.set(canvasW * vpRect.x, canvasH * vpRect.y, canvasW * vpRect.w, canvasH * vpRect.h);
            //viewProjMtx 计算
            const camPMtx = cam.getProjectionMatrx();
            const camVMtx = cam.getViewMatrix();
            camVMtx.multiplyToRef(camPMtx, renderCtx.camViewProjMatrix);
            //相机视锥顶点计算
            this.calcCamFrustumPoints(cam, renderCtx);
            //相机UBO 设置
            const camUBO = cam.ubo;
            cam.refreshUboData();
            gl.bindBufferRange(gl.UNIFORM_BUFFER, UBOBindingEnum.CAMERA, camUBO, 0, cam.uboBlockSize);
        }

        /**
         * 渲染 相机 帧清理
         */
        private renderCameraFrameClear(cam: Camera, renderCtx: RenderContext) {
            const gl = renderCtx.webgl2;
            const bg = cam.backGround;
            this.setDepthState(DepthState.getState(true));  //zwrite true  才能清理 depth
            gl.clearDepth(1.0);
            switch (cam.clearMode) {
                case CameraClearMode.SKY_BOX:
                case CameraClearMode.COLOR:
                    gl.clearColor(bg.r, bg.g, bg.b, bg.a);    //backclear color
                    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                    break;
                case CameraClearMode.DEPTH_ONLY:
                    gl.clear(gl.DEPTH_BUFFER_BIT);
                    break;
                default:
            }

        }

        /** 临时renderTexture2D 对象清理 */
        private rtTemporaryRelease() {
            //相机深度rt
            if (this._camDepthTex) {
                RenderTexture2D.ReleaseTemporary(this._camDepthTex);
                this._camDepthTex = null;
            }

            //屏幕空间阴影 rt
            if (this._screenShadowTex) {
                RenderTexture2D.ReleaseTemporary(this._screenShadowTex);
                this._screenShadowTex = null;
            }

            //mainShadowMap rts
            this.rtListRelease(this._mainShadowMapList);
            this.rtListRelease(this._mainVSMList);
        }

        //清理 rt列表
        private rtListRelease(list: RenderTexture2D[]) {
            if (!list || list.length < 1) return;
            for (let i = 0, len = list.length; i < len; i++) {
                const st = list[i];
                if (!st) continue;
                RenderTexture2D.ReleaseTemporary(st);
            }
            list.length = 0;
        }

        /** 渲染到纹理 前期操作 */
        private renderImagePro(renderCtx: RenderContext, srcRT: RenderTexture2D, destRT: RenderTexture2D) {
            let gl = renderCtx.webgl2;
            const viewPort = renderCtx.curCamPixelViewPort;
            srcRT.setSize(viewPort.w, viewPort.h);
            destRT.setSize(viewPort.w, viewPort.h);

            //清理 dest buffer
            this.enableFBO(destRT.fbo);
            gl.clearColor(0, 0, 0, 0);    //backclear color
            gl.clear(gl.COLOR_BUFFER_BIT);
            //切换到 srcRT 接收 pass的绘制
            this.enableFBO(srcRT.fbo);
        }

        /** 渲染到纹理 后期操作 */
        private renderImagePost(renderCtx: RenderContext, renderImages: IOnRenderImage[], srcRT: RenderTexture2D, destRT: RenderTexture2D) {
            //执行 后渲染到纹理
            for (let j = 0, len1 = renderImages.length; j < len1; j++) {
                let c = renderImages[j];
                c.onRenderImage(srcRT, destRT);
            }

            //最终渲染到屏幕
            this._fullScreenImageMtr.setProperty("u_mainTex", destRT);
            this.renderPostEffQuad(renderCtx, this._fullScreenImageMtr, renderCtx.curCamPixelViewPort);
        }

        /** 渲染天空盒 到纹理 */
        private renderSkyBox(renderCtx: RenderContext) {
            const material = renderCtx.skyboxMaterial;
            if (!material) return;
            //准备好 天空盒的 cubemap 、贴满clip空间的mesh、天空盒的着色器
            const gl = renderCtx.webgl2;
            const sh = material.shader;
            const cSH = sh.getCompiled(material.getKeywordID()) as ShaderCompiled;
            const sp = cSH.program as WebGLProgram;
            const quadMesh = this._fullClipQuadMesh;

            //执行绘制
            //WebGLProgram use
            gl.useProgram(sp);
            //vao
            gl.bindVertexArray(quadMesh.VAO);   //启用vao
            //纹理Activator
            renderCtx.textrueActivator.reset();
            //uniform 数据提交
            material.uploadUniform(renderCtx);

            //绘制贴屏片 后渲染效果
            gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);

            //绘制完毕清理状态
            this.closeGLState();
        }

        /** 收集相机的 RenderImage组件 */
        private collectCamRenderImage(cam: Camera, renderImages: IOnRenderImage[]) {
            //onRenderImage 收集
            renderImages.length = 0;

            const camGameObject = (cam.entity as GameObject);
            for (let ii = 0, len1 = camGameObject.componentCount; ii < len1; ii++) {
                let c = camGameObject.getComponent(ii);
                if (!(c as any)[I_ON_RENDER_IMAGE_FUN]) continue;
                //renderImage comp
                renderImages.push(c as any);
            }
        }

        public render(renderCtx: RenderContext, cameras: Camera[]): void {
            super.render(renderCtx, cameras);
            this._currRenderCtx = renderCtx;
            const rendererMap = renderCtx.rendererMap;
            if (!rendererMap) {
                console.warn(`rendererMap is null.`);
                return;
            }
            const gl = App.webgl2;
            //管线初始化设置
            //设置 材质ubo忽略 block（内建 部分 UBO需要设置为忽略）
            this._ignBlcokNames.forEach((str) => {
                renderCtx.ignoreInMaterialBlockMap[str] = true;
            });
            //内建uniform 绑定
            this.bindBuiltinUniforms(renderCtx, false);
            //
            const cpRendererIDs = this._cullPassRendererIDs;
            cpRendererIDs.length = 0;

            //灯光参数设置提交
            const lightUBO = renderCtx.lightCollector.ubo;
            renderCtx.lightCollector.refreshUboData();
            gl.bindBufferRange(gl.UNIFORM_BUFFER, UBOBindingEnum.LIGHT, lightUBO, 0, renderCtx.lightCollector.uboBlockSize);

            //天空盒设置
            if (renderCtx.skyboxMaterial) {
                const skyboxName = `u_skybox`;
                const skyboxTex = renderCtx.skyboxMaterial.getProperty(skyboxName) as TextureCube;
                Shader.setProperty(skyboxName, skyboxTex);
                //skybox 强制开mipmap
                if (skyboxTex && !skyboxTex.paramSetting.mipmap) {
                    skyboxTex.paramSetting.mipmap = true;
                    skyboxTex.apply();
                }
            }

            //renderImage rt
            let srcRT: Nullable<RenderTexture2D> = null;
            let destRT: Nullable<RenderTexture2D> = null;

            //相机loop
            for (let i = 0, len = cameras.length; i < len; i++) {
                const cam = cameras[i];
                //相机初始化设置
                //使用相机
                this.useCamera(cam, renderCtx);

                let renderImages = this._renderImageComps;
                //onRenderImage 收集
                this.collectCamRenderImage(cam, renderImages);

                const needRenderImage = renderImages.length > 0;
                //屏幕颜色 纹理初始化
                if (needRenderImage && (!srcRT || !destRT)) {
                    srcRT = RenderTexture2D.GetTemporary();
                    srcRT.setFBTexEnable(true, true, true);
                    destRT = RenderTexture2D.GetTemporary();
                    destRT.setFBTexEnable(true, false, false);
                }

                //清理状态
                this._rendererMInfoMap.forEach((v, k) => { if (v) RendererMaterialInfo.pool.delete(v); });
                this._rendererMInfoMap.clear();
                this._shadowCasterRList.length = 0;
                //相机 cull
                this.cameraFrustumCulling(cam, rendererMap, cpRendererIDs);
                //材质组预处理和分析
                this.materialGroupAnalyse(rendererMap, cpRendererIDs, this._rendererMInfoMap, this._shadowCasterRList);
                //是否使用 相机深度纹理
                let useCamDepthTex = this._shadowCasterRList.length > 0;

                if (useCamDepthTex) {
                    //相机深度图更新
                    this.renderCamDepthTex(renderCtx);
                }
                //渲染阴影生成通道
                this.renderShadowCasterPass(renderCtx);

                //前向渲染管线渲染
                if (this._drawDebugFrame) {
                    //渲染 debug帧
                    this.renderDebugFrame(renderCtx, this._drawDebugFrame);
                } else {
                    //renderImage 渲染前处理
                    if (needRenderImage && srcRT && destRT) {
                        this.renderImagePro(renderCtx, srcRT, destRT);
                    }

                    //相机帧缓冲清理
                    this.renderCameraFrameClear(cam, renderCtx);
                    //执行渲染pass
                    this.renderForwardPass(renderCtx, cam);

                    //绘制天空盒
                    if (cam.clearMode == CameraClearMode.SKY_BOX) {
                        this.renderSkyBox(renderCtx);
                    }
                }

                //后渲染到纹理
                if (needRenderImage && srcRT && destRT) {
                    this.renderImagePost(renderCtx, renderImages, srcRT, destRT);
                }
            }

            //管线清理
            RenderTexture2D.ReleaseTemporary(srcRT as any);
            RenderTexture2D.ReleaseTemporary(destRT as any);
            this.rtTemporaryRelease();

            //还原 材质ubo忽略 block 设置
            this._ignBlcokNames.forEach((str) => {
                renderCtx.ignoreInMaterialBlockMap[str] = false;
            });
            //内建uniform 绑定解除
            this.bindBuiltinUniforms(renderCtx, true);

            //final
            (this._currRenderCtx as any) = null;
        }

    }
}